package org.awa.demo.opencv.service.impl;

import cn.hutool.core.util.RandomUtil;
import org.awa.demo.opencv.bean.gltf.*;
import org.awa.demo.opencv.gltf.GltfEncoder;
import org.awa.demo.opencv.gltf.SoundUtil;
import org.awa.demo.opencv.gltf.handler.IRectangleHandler;
import org.awa.demo.opencv.gltf.handler.RectangleAnalysis;
import org.awa.demo.opencv.gltf.handler.RectangleAssigner;
import org.awa.demo.opencv.service.IGltfService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.File;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class GltfServiceImpl implements IGltfService {

    @Autowired
    private GltfEncoder gltfEncoder;

    @Autowired
    private RectangleAssigner assigner;

    @Autowired
    private RectangleAnalysis analysis;

    @Override
    public GltfRequestDto getGltf() throws Exception {
        GltfRequestDto requestDto = new GltfRequestDto();
        requestDto.setGltfPath("http://115.236.86.166:9000/doc/bangongshiv2.glb");
        requestDto.setGltfUVPath("http://115.236.86.166:9000/doc/bangongshiv2-uv.glb");

        String filePath = "E:\\blenderworkspace\\bangongshiv2\\bangongshiv2-uv.glb";
        //解析glb文件，获取面列表以及相关信息
        List<PrimitiveBean> primitiveList = getPrimitiveList(filePath);
        if (primitiveList.size() == 0) {
            requestDto.setFaceList(new ArrayList<>());
            return requestDto;
        }

        List<FaceItemDto> faceList = new ArrayList<>();

        for (int i = 0; i < primitiveList.size(); i++) {
            PrimitiveBean primitiveBean = primitiveList.get(i);
            //获取面轮廓数据以及网格位置数据
            IRectangleHandler rectangleHandler = assigner.getRectangleHandler(primitiveBean.getBlenderPointList());
            FaceItemDto face = analysis.getFace(primitiveBean, rectangleHandler);
            faceList.add(face);
        }
        requestDto.setFaceList(faceList);

        //获取测点相关信息
        List<MeasureItemDto> measureList = getMeasureList(faceList);
        requestDto.setMeasureList(measureList);

        for (int i = 0; i < faceList.size(); i++) {
            FaceItemDto face = faceList.get(i);
            //根据网格获取leqt数据
            calLeqtInFace(face, measureList);
        }

        return requestDto;
    }

    private void calLeqtInFace(FaceItemDto faceItemDto, List<MeasureItemDto> measureList) {
        List<GridItemDto> gridList = faceItemDto.getGridList();
        for (int i = 0; i < gridList.size(); i++) {
            GridItemDto gridItemDto = gridList.get(i);
            PointItemDto gridPoint = SoundUtil.getPointBean(gridItemDto.getPointCrs());

            List<GridMeasureDistanceItemDto> distanceListInMeasure = measureList.stream().map(measure -> {
                GridMeasureDistanceItemDto itemDto = new GridMeasureDistanceItemDto(measure, gridPoint);
                itemDto.setDistance(SoundUtil.getDistance(measure, gridPoint));
                return itemDto;
            }).sorted(Comparator.comparing(GridMeasureDistanceItemDto::getDistance)).collect(Collectors.toList());

            List<Double> leqtList = filterGridMeasure(distanceListInMeasure).stream().map(gridMeasure -> {
                return SoundUtil.calAttenuationLeptPointBase(gridMeasure.getDistance(), gridMeasure.getMeasure().getLeqt());
            }).collect(Collectors.toList());

            Double leqt = SoundUtil.soundSuperposition(leqtList);
            gridItemDto.setLeqt(BigDecimal.valueOf(leqt).setScale(1, RoundingMode.HALF_DOWN).doubleValue());
        }
    }

    private List<GridMeasureDistanceItemDto> filterGridMeasure(List<GridMeasureDistanceItemDto> gridMeasureList) {
        if (gridMeasureList == null || gridMeasureList.size() == 0) {
            return new ArrayList<>();
        }

        GridMeasureDistanceItemDto itemDto = gridMeasureList.get(0);
        if (itemDto.getDistance() < 4) {
            List<GridMeasureDistanceItemDto> result = new ArrayList<>();
            result.add(itemDto);
            return result;
        }

        return gridMeasureList;
    }

    private List<PrimitiveBean> getPrimitiveList(String filePath) throws Exception {
        File file = new File(filePath);
        if (!file.exists()) {
            return new ArrayList<>();
        }

        return gltfEncoder.encode(file);
    }

    public List<MeasureItemDto> getMeasureList(List<FaceItemDto> faceList) throws Exception {
        //根据轮廓值获取3维范围
        if(faceList == null || faceList.size() == 0) {
            return new ArrayList<>();
        }
        BigDecimal[] range = getRange(faceList);

        List<MeasureItemDto> resultList = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            MeasureItemDto measureItemDto = new MeasureItemDto();

            measureItemDto.setX(RandomUtil.randomBigDecimal(range[0],range[1]).setScale(2,RoundingMode.HALF_DOWN).floatValue());
            measureItemDto.setY(RandomUtil.randomBigDecimal(range[2],range[3]).setScale(2,RoundingMode.HALF_DOWN).floatValue());
            measureItemDto.setZ(RandomUtil.randomBigDecimal(range[4],range[5]).setScale(2,RoundingMode.HALF_DOWN).floatValue());
            measureItemDto.setLeqt(randomLeqt());

            resultList.add(measureItemDto);
        }
        return resultList;
    }

    private Double randomLeqt() {
        return BigDecimal.valueOf(RandomUtil.randomInt(500, 900)).divide(BigDecimal.valueOf(10), 1, RoundingMode.HALF_DOWN).doubleValue();
    }

    private BigDecimal[] getRange(List<FaceItemDto> faceList){
        BigDecimal xMin = null;
        BigDecimal xMax = null;
        BigDecimal yMin = null;
        BigDecimal yMax = null;
        BigDecimal zMin = null;
        BigDecimal zMax = null;

        for (int i = 0; i < faceList.size(); i++) {
            FaceItemDto faceItemDto = faceList.get(i);
            for (int j = 0; j < faceItemDto.getScopeList().size(); j++){
                PointItemDto pointItemDto = faceItemDto.getScopeList().get(j);
                BigDecimal x = BigDecimal.valueOf(pointItemDto.getX());
                BigDecimal y = BigDecimal.valueOf(pointItemDto.getY());
                BigDecimal z = BigDecimal.valueOf(pointItemDto.getZ());

                if(xMin == null || xMin.compareTo(x) > 0){
                    xMin = x;
                }
                if(xMax == null || xMax.compareTo(x) < 0){
                    xMax = x;
                }
                if(yMin == null || yMin.compareTo(y) > 0){
                    yMin = y;
                }
                if(yMax == null || yMax.compareTo(y) < 0){
                    yMax = y;
                }
                if(zMin == null || zMin.compareTo(z) > 0){
                    zMin = z;
                }
                if(zMax == null || zMax.compareTo(z) < 0){
                    zMax = z;
                }
            }
        }
        return new BigDecimal[]{
            xMin, xMax, yMin, yMax, zMin, zMax
        };
    }
}
